package drds.plus.sql_process.parser.visitor.dml;

import drds.plus.parser.abstract_syntax_tree.expression.Expression;
import drds.plus.parser.abstract_syntax_tree.expression.Pair;
import drds.plus.parser.abstract_syntax_tree.expression.primary.misc.ParameterMarker;
import drds.plus.parser.abstract_syntax_tree.statement.SelectStatement;
import drds.plus.parser.abstract_syntax_tree.statement.UnionStatement;
import drds.plus.parser.abstract_syntax_tree.statement.select.GroupBy;
import drds.plus.parser.abstract_syntax_tree.statement.select.Limit;
import drds.plus.parser.abstract_syntax_tree.statement.select.Order;
import drds.plus.parser.abstract_syntax_tree.statement.select.OrderBy;
import drds.plus.parser.abstract_syntax_tree.statement.select.table.ITable;
import drds.plus.parser.abstract_syntax_tree.statement.select.table.Tables;
import drds.plus.parser.visitor.EmptyVisitor;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.LockMode;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Filter;
import drds.plus.sql_process.abstract_syntax_tree.node.query.$Query$;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.parser.visitor.ExpressionVisitor;

import java.util.ArrayList;
import java.util.List;

/**
 * select表达式的解析
 */
public class SelectVisitor extends EmptyVisitor {

    private Query parent;
    private Query query;


    public SelectVisitor() {

    }


    public SelectVisitor(Query parent) {
        this.parent = parent;
    }

    public Query getQuery() {
        if (parent != null && query != null) {
            query.setParent(parent);
        }

        return query;
    }

    public void visit(SelectStatement selectStatement) {
        Tables tables = selectStatement.getTables();
        if (tables != null) {
            tables(tables);
        }

        List<Pair<Expression, String>> selectItemPairList = selectStatement.getSelectItemPairList();
        if (selectItemPairList != null) {
            List<Item> selectItemList = selectItemList(selectItemPairList);
            selectItemOption(selectStatement, selectItemList);
            if (selectItemList != null) {
                query.setSelectItemListAndSetNeedBuild(selectItemList);
            }
        }

        Expression where = selectStatement.getWhere();
        if (where != null) {
            where(where);
        }


        GroupBy groupBy = selectStatement.getGroupBy();
        if (groupBy != null) {
            groupBy(groupBy);
        }

        Expression having = selectStatement.getHaving();
        if (having != null) {
            having(having);
        }
        OrderBy orderBy = selectStatement.getOrderBy();
        if (orderBy != null) {
            orderBy(orderBy);
        }
        Limit limit = selectStatement.getLimit();
        if (limit != null) {
            limit(limit);
        }
    }

    public void visit(UnionStatement unionStatement) {
        throw new UnsupportedOperationException();
    }


    private List<Item> selectItemList(List<Pair<Expression, String>> pairList) {
        List<Item> itemList = new ArrayList<Item>();
        for (Pair<Expression, String> pair : pairList) {
            Expression expression = pair.getKey();
            ExpressionVisitor expressionVisitor = new ExpressionVisitor(this.query);
            expression.accept(expressionVisitor);
            Object object = expressionVisitor.getObject();
            if (!(object instanceof Item)) { // 常量先转成booleanFilter
                object = expressionVisitor.buildConstantFilter(object);
                if (expressionVisitor.getExpressionSqlString() != null) {
                    ((Item) object).setColumnName(expressionVisitor.getExpressionSqlString());
                }
            }
            ((Item) object).setAlias(pair.getValue());
            itemList.add((Item) object);
        }

        return itemList;
    }

    private void selectItemOption(SelectStatement selectStatement, List<Item> itemList) {
        if (selectStatement.isDistinct()) {
            for (Item item : itemList) {
                item.setDistinct(true);
            }
        }
        if (this.query == null) {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
        if (selectStatement.isForUpdate()) {
            query.setLockMode(LockMode.exclusive_lock);
        } else if (selectStatement.isLockInShareMode()) {
            query.setLockMode(LockMode.shared_lock);
        }
    }

    private void tables(Tables tables) {
        List<ITable> tableList = tables.getTableList();
        for (int i = 0; i < tableList.size(); i++) {
            //table对象可能是join
            ITable table = tableList.get(i);
            ExpressionVisitor expressionVisitor = new ExpressionVisitor();
            table.accept(expressionVisitor);
            //
            if (this.query == null) {
                this.query = expressionVisitor.getQuery();
                // 如果是第一个table，并且是唯一的一个，才做queryNode，因为如果多于两个可以通过joinNode来代替
                if (this.query.isSubQuery() && i == tableList.size() - 1) {
                    this.query = new $Query$(this.query);
                }
            } else {
                this.query = this.query.join(expressionVisitor.getQuery());
            }
        }
    }

    private void where(Expression where) {
        ExpressionVisitor expressionVisitor = new ExpressionVisitor(this.query);
        where.accept(expressionVisitor);
        if (this.query != null) {
            Filter whereFilter = null;
            if (expressionVisitor.getFilter() != null) {
                whereFilter = expressionVisitor.getFilter();
            } else if (expressionVisitor.getObject() != null) {
                whereFilter = expressionVisitor.buildConstantFilter(expressionVisitor.getObject());
            }
            //所有条件都在这
            query.setWhereAndSetNeedBuild(whereFilter);
            this.query.setAllWhereFilter(query.getWhere());
        } else {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
    }


    private void groupBy(GroupBy groupBy) {
        if (this.query == null) {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
        List<Pair<Expression, Order>> orderByList = groupBy.getGroupByList();
        for (Pair<Expression, Order> pair : orderByList) {
            Expression expression = pair.getKey();
            ExpressionVisitor expressionVisitor = new ExpressionVisitor(this.query);
            expression.accept(expressionVisitor);
            query.addGroupByItemAndSetNeedBuild((Item) expressionVisitor.getObject(), pair.getValue() == Order.asc);
        }

    }

    private void having(Expression having) {
        if (this.query == null) {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
        ExpressionVisitor expressionVisitor = new ExpressionVisitor(this.query);
        having.accept(expressionVisitor);
        Filter havingFilter = expressionVisitor.getFilter();
        if (havingFilter != null) {
            this.query.having(havingFilter);
        } else if (expressionVisitor.getObject() != null) {
            this.query.having(expressionVisitor.buildConstantFilter(expressionVisitor.getObject()));
        }

    }

    private void orderBy(OrderBy orderBy) {
        if (this.query == null) {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
        List<Pair<Expression, Order>> orderByList = orderBy.getOrderByList();
        for (Pair<Expression, Order> pair : orderByList) {
            Expression expression = pair.getKey();
            ExpressionVisitor expressionVisitor = new ExpressionVisitor(this.query);
            expression.accept(expressionVisitor);
            query.addOrderByItemAndSetNeedBuild((Item) expressionVisitor.getObject(), pair.getValue() == Order.asc);
        }
    }

    private void limit(Limit limit) {
        if (this.query == null) {
            throw new IllegalArgumentException("from expression is null,check the chars!");
        }
        if (limit.getOffset() instanceof ParameterMarker) {
            query.setLimitFrom(ObjectCreateFactory.createBindValue(((ParameterMarker) limit.getOffset()).getParameterIndex()));
        } else {
            query.setLimitFrom(Long.valueOf(String.valueOf(limit.getOffset())));
        }

        if (limit.getSize() instanceof ParameterMarker) {
            query.setLimitTo(ObjectCreateFactory.createBindValue(((ParameterMarker) limit.getSize()).getParameterIndex()));
        } else {
            query.setLimitTo(Long.valueOf(String.valueOf(limit.getSize())));
        }
    }

}
